home *** CD-ROM | disk | FTP | other *** search
- Copyright (c) 1991-1993 Borland International, Inc.
- All Rights Reserved.
-
- THE dBASE LANGUAGE OBJECT EXTENSIONS
- ------------------------------------
- Application programmers are facing increasingly complex
- programming requirements. For instance, building event-driven
- Windows applications often means learning new techniques and
- coordinating among several programmers writing many lines of
- source code. To make your job easier, Bladerunner has enhanced
- the dBASE language with new object oriented extensions.
-
- This document introduces the object extensions and describes some
- of the new techniques for using them. Another file, DBLANG.WRI,
- contains specifications and syntax for the new extensions.
-
- Contents
- --------
-
- I. Object Terminology
- II. Who Needs Object Extensions?
- III. Object-Orientated Design in dBASE
- IV. Variable Scoping and Objects
- V. Creating Objects
- VI. Understanding Object Reference Variables
- VII. Understanding Function Pointers
- VIII. Creating New Classes
- IX. Examples
-
- Object Terminology
- ------------------
-
- Before learning how object orientation applies to dBASE, you need
- to become familiar with certain standard terminology of object-
- oriented programming. The following definitions will give you an
- idea of how these concepts are implemented in Bladerunner.
-
- Object A collection of related memory variables. Objects
- consist of properties and methods. An object is an
- "instance" of a class. For example, a window you
- create, with DEFINE WINDOW, is an instance of the
- Window class.
-
- Class A specification, or "recipe," for a type of
- object. A class definition consists of two parts:
- first, any dBASE code required for the construction of
- an object, such as property assignments; second, method
- declarations. Bladerunner provides many pre-defined
- "stock" classes, such as Window and Menu. (The list of
- stock classes appears in the on-line help.) You can also
- create your own classes with the CLASS...ENDCLASS
- statement.
-
- Member An item contained in an object. An object's
- members are properties and methods.
-
- Property A memory variable contained in an object.
- Properties define characteristics of an object, such as
- size, location, or color. You can query the value of
- any property, and change the value of most properties.
- You can add new properties to an object with a simple
- assignment statement.
-
- Method A subroutine, such as a function or procedure,
- associated with an object through a function-pointer
- variable. Methods perform actions on an object. For
- example, a method can change an object's position in a
- window, or determine if the value of a property is
- valid. Some methods execute automatically when an event
- occurs; others execute only when called explicitly.
-
- Event An occurrence, such as a mouse click. Events cause
- methods to execute. For example, pressing the left
- mouse button while pointing to a window object executes
- the window's OnLeftMouseDown method.
-
-
- Who Needs Object Extensions?
- -------------------------------
-
- Using traditional dBASE commands and techniques, you can easily
- build procedural user interfaces, where a series of cascading
- menu choices launch pre-defined procedures. However, these
- techniques are not well-suited for creating event-driven
- interfaces. In event-driven environments, like Windows, the user
- controls program flow. A user can click a button, activate a
- window, or select a menu choice at any time, and the program must
- respond to these events in whatever sequence they occur.
-
- To help build event-driven applications, Bladerunner provides UI
- language extensions (described in UI_EXTEN.TXT). Using familiar
- dBASE-like syntax, any dBASE programmer can define UI objects,
- called "controls," and activate them in one or more windows.
-
- Advanced programmers need the ability to change a control's
- characteristics in response to an event. For instance, after
- defining and displaying a window, your program might need to
- change the window's size, position, or color, without destroying
- the window and creating it again. The Bladerunner UI extensions
- let you change certain control characteristics using the REDEFINE
- commands. However, REDEFINE can't change all characteristics of a
- control, tell you the value of a characteristic, or add new
- characteristics.
-
- Developers writing sophisticated user interfaces need the ability
- to change, query, and even add, any characteristic to a control.
- The Bladerunner object extensions make this easy.
-
-
-
- Object-Oriented Design in dBASE
- -------------------------------
-
- This section introduces some of the concepts behind object-
- oriented programming, then shows how you can apply them to dBASE.
- Focus on the concepts here; the details for implementing them are
- explained later.
-
- Three main concepts characterize an object-oriented programming
- environment:
-
- Encapsulation Combining data with the subroutines that operate
- on it to form a new structure called an object.
-
- Inheritance Defining a type, or class, of object and then
- using it to build a hierarchy of descendant
- objects, with each descendant "inheriting" the
- data and subroutines of its ancestors.
-
- Polymorphism Giving an object in a hierarchy the ability to
- implement an action that is common to all objects
- in the hierarchy, in whatever way is most
- appropriate for that object.
-
- Suppose you write programs for a small business. In one of your
- applications, you create a window for displaying employee
- records, and another for displaying customer information. For
- each window, you store field values to memory variables for
- displaying and editing.
-
- Next, suppose you want to display both windows at the same time.
- Doing so, you risk having employee variables conflict with
- variables defined for the customer information window. For
- instance, if you display the address for both employees and
- customers, you may overwrite the City, State, or Zip variables of
- an employee with those of a customer.
-
- To solve this, you can encapsulate the employee data, and the
- subroutines for manipulating it, into an Employee object.
- Likewise, you can encapsulate the customer data and subroutines
- into a Customer object. Then, instead of displaying a regular
- memory variable for each address item, you display the City,
- State, and Zip "properties" of the Employee or Customer objects.
-
- Encapsulation "hides" data in an object; so the properties of the
- Employee object can co-exist with the properties of the Customer
- object. When you encapsulate data into objects, you reduce the
- risk that one programmer's work will interfere with another's.
-
- Next, suppose you want to create a new window for editing
- prospective customers who haven't yet placed an order. You need
- to store the same information as a customer, plus some new
- fields. Instead of creating a new window from scratch, you can
- create a Prospect object based on the Customer object. The
- Prospect object inherits the characteristics of the Customer
- object.
-
- Inheritance is essentially programming by behavior modification.
- Different objects that share some common data or behavior can
- both inherit that information from an existing object, and then
- you can change only what's needed. Inheritance makes your code
- much easier to re-use.
-
- Next, suppose you need to display information for both full-time
- and part-time employees. Both employee types need to store the
- same information, except that part-timers don't get vacation pay.
- You can start by creating a PartTime object based on the Employee
- object. The Employee object has a subroutine, CalculatePay(), for
- calculating the yearly compensation. In the PartTime object, you
- can override CalculatePay() with another function that excludes
- vacation pay. This is an example of polymorphism.
-
- The dBASE SKIP command illustrates the benefits of polymorphism.
- SKIP works differently depending on the status of the current
- work area. If the work area is indexed, SKIP moves to the next
- record in the index; if a filter is set, SKIP moves to the next
- record meeting the filter condition. You don't need to tell SKIP
- about the work area; you just SKIP and it knows what to do.
-
- Likewise, your program can retrieve the yearly compensation of an
- employee the same way, by invoking CalculatePay(), regardless of
- the type of employee. By "hiding" the implementation of an
- action, you are free to change the details of the implementation
- without requiring changes to the interface. Suppose full-timers
- get a quarterly bonus; you just change CalculatePay() for the
- Employee object. Polymorphism enhances the reusability of your
- code.
-
- Variable Scoping and Objects
- ----------------------------
-
- To understand how Bladerunner encapsulates variables into
- objects, you need to understand variable scoping. This section
- introduces objects as a solution to common dBASE scoping
- problems.
-
- The scope of a memory variable is defined by
-
- - the variable's lifespan; that is, under what circumstances a
- variable is released or destroyed.
-
- - the variable's visibility; that is, under what circumstances a
- program can access or modify a variable.
-
- Bladerunner offers two new ways to specify memory variable
- scopes. First, the new LOCAL command declares variables to be
- local to the procedure or function in which they are created.
- LOCAL variables are similar to PRIVATE, except that LOCAL
- variables are not visible in subsequently called routines.
- Second, the new STATIC command declares variables that are LOCAL
- in visibility but PUBLIC in lifespan.
-
- The following table summarizes the lifespan and visibility of
- dBASE variable types:
-
- Lifespan Visibility
- ---------------------------- -----------------------
- PUBLIC Destroyed only when released Everywhere
-
- PRIVATE Destroyed when creating Creating routine, and
- routine ends subsequent routines
-
- LOCAL Destroyed when creating Creating routine only
- routine ends
-
- STATIC Destroyed only when released Creating routine only
-
- You can declare LOCAL and STATIC variables in your procedural
- code to protect against inadvertant overwriting of data, and
- increase program modularity. Also, when you create objects,
- variables that are members of an object are automatically LOCAL
- in scope.
-
-
- Creating objects
- ----------------
-
- Create an object using the NEW operator in a memory variable
- assignment statement. NEW creates a new object, and creates an
- "object reference" variable that refers to the object. (See
- "Understanding Object Reference Variables" below.) The following
- example creates a new Window object and makes MyWin refer to it:
-
- MyWin = NEW Window()
-
- Use the member access operator (.) to refer to a member of an
- object. The member access operator, or "dot" operator, associates
- members to an object much the same way an alias operator (->)
- associates a field with a particular table.
-
- MyWin.Left = 200 && Move the left border to position 200.
- MyWin.Visible = .F. && Make MyWin invisible
-
- Objects are extensible. You can add new members to an object with
- a simple assignment statement.
-
- MyWin.MyPropN = 123 && Adds a new property called MyPropN
- MyWin.MyPropC = "yo" && Adds a new property called MyPropC
-
- Understanding Object-Reference Variables
- ----------------------------------------
-
- Traditional dBASE memory variables contain values; a numeric
- variable contains a numeric value. In contrast, an object-
- reference variable contains a reference to an object, not the
- object itself.
-
- You create a new object-reference variable the same way you
- create other variables, with a simple assignment statement. To
- create a new object, however, use the NEW operator in an
- assignment statement.
-
- This example demonstrates normal dBASE memory variable
- assignments:
-
- X = 5 && X contains the value 5
- Y = X && Y contains the value 5
- X = 6 && X contains the value 6
- ? X && Returns 6
- ? Y && Returns 5
-
- Now compare the previous example to the following example, using
- object-reference variables:
-
- MyWin = NEW Window() && Create a Window object and create
- && MyWin referring to the window.
- ? TYPE("MyWin") && Returns "O" for object reference
- MyWin.X = 10 && Add new property X
- MyWin.Y = 20 && Add new property Y
- YourWin = MyWin && MyWin and YourWin both refer to
- && the same window
- ? YourWin.X && Returns 10
- YourWin.Y = 30 && Changes property Y to 30
- ? YourWin.Y && Returns 30
- ? MyWin.Y && Returns 30
- YourWin = "text" && YourWin is now a normal char variable
- ? MyWin.Y && Still returns 30
- MyWin = "text" && MyWin is now a normal char variable
- && The window object is destroyed
-
- The previous example demonstrates three important points about
- object-reference variables:
-
- - More than one object-reference variable can refer to the same
- object.
-
- - When you change an object, all references to the object reflect
- the change.
-
- - An object exists as long as an object-reference variable refers
- to it. When you release or re-assign all object-reference
- variables referring to an object, the object is destroyed.
-
- You work with object-reference variables the same way you work
- with other memory variables. For instance, you can pass them as
- parameters, return them as function results, and store them in
- arrays.
-
- Understanding Function Pointers
- -------------------------------
-
- A method is a subroutine associated with an object through a
- function pointer variable, a new variable type. Function pointers
- refer indirectly to a function. They can be copied, passed as
- parameters, returned as values from functions, and used in the
- same way as traditional variables.
-
- Function pointers call functions indirectly using the call
- operator "()". For example:
-
- pFunc = Ten && Assigns Function Ten to pFunc
- ? pFunc() && Returns 10
-
- Function Ten
- RETURN 10
-
-
- Creating New Classes
- --------------------
-
- You can define a new class using the CLASS...ENDCLASS statement.
- The following example defines a class with two properties,
- creates an object of that class, and queries the property values:
-
- X = NEW Numbers() && Creates a new Numbers object
- ? X.Ten && Returns 10
- ? X.Twenty && Returns 20
-
- CLASS Numbers
- MEMBER Ten, Twenty
- Ten = 10
- Twenty = 20
- ENDCLASS
-
- Using MEMBER or.This to Reference Properties
-
- The MEMBER statement in class definitions declares properties.
- Bladerunner also provides an alternate syntax for referencing
- properties in classes without explicitly declaring them. To
- reference properties not declared in a MEMBER statement, preface
- the property name with "This.".
-
- This. acts like a place holder for the object name created from
- the class.
-
- The following two class definitions are semantically identical:
-
- A = NEW Square2()
- A.Num = 5
- ? A.Value() && Returns 25
-
- CLASS Square1
- MEMBER Num
- Num = 0
- FUNCTION Value
- RETURN Num * Num
- ENDCLASS
-
- CLASS Square2
- This.Num = 0
- FUNCTION Value
- RETURN This.Num * This.Num
- ENDCLASS
-
-
- Defining and Passing Parameters
-
- You define and pass parameters for classes by placing the
- parameters in parentheses at the end of the class name.
- Parameters you pass are LOCAL in scope. The following example
- defines a class, Square, and passes a parameter when creating a
- Square object:
-
- X = NEW Square(10) && Creates a new Square object
- ? X.SquareNum && Returns 100
- Y = NEW Square(5) && Creates a new Square object
- ? Y.SquareNum && Returns 25
-
- CLASS Square(n)
- MEMBER Num, SquareNum
- Num = n
- SquareNum = n*n
- ENDCLASS
-
- Note: In this Alpha release, declaring parameters in parentheses
- is not implemented. You can instead declare parameters with a
- PARAMETERS statement as follows:
-
- CLASS SQUARE
- PARAMETERS n
- ENDCLASS
-
- Defining and calling methods
-
- You define methods for a class using a FUNCTION declaration
- within the class definition. Follow the same rules for declaring
- normal dBASE UDFs.
-
- In the previous Square example, the value of property X.SquareNum
- is computed when you create the object. By declaring Value as a
- method instead of a property, you can compute Value at any time.
- The following example demonstrates this:
-
- X = NEW Square2() && Create a new Square2 object
- X.Num = 5 && Change the value of Num
- ? X.Square() && Returns 25
- X.Num = 6 && Change the value of Num again
- ? X.Square() && Returns 36
-
- CLASS Square2
- MEMBER num
- Num = 0
- FUNCTION SquareNum
- RETURN Num * Num
- ENDCLASS
-
- Note: If you assign the same name to a method and a property, the
- property is assumed in expressions. Internally, Bladerunner
- treats methods and properties the same; a method is a property
- whose data type is function-pointer.
-
- This example assigns the name Ten to both a method and a
- property. Notice the return values:
-
- Function Ten
- RETURN 10
- Ten = 10
- pFunc = Ten && assigns value 10 to pFunc
- ? pFunc() && ERROR: data type mismatch
-
- A subroutine can serve as a method for more than one object. The
- following example demonstrates this:
-
- O = NEW Object()
- O.x = 10
- O.addOne = FuncAddOne
- ? O.addOne() && Returns '11'
- ? O.x && Returns '11'
- Y = NEW Object()
- Y.x =30
- Y.z = FuncAddOne
- ? Y.z() && Returns '31'
- FUNCTION FuncAddOne
- this.x = this.x + 1
- RETURN this.x
-
-
- Examples
- --------
-
- Creating a class based on another class - Inheritance
-
- You can create a class that is derived from, or "inherits," the
- members of another class. The parent class can be a stock class,
- such as Window, or a class you previously created. This technique
- is called sub-classing and demonstrates the concept of
- inheritance. Subclasses inherit all properties and methods of the
- parent from which they are derived.
-
- Use the OF option in the CLASS definition statement to specify
- the parent class from which to inherit members. The following
- example defines a class called Parent, and derives another class
- from Parent. The Derived class inherits all the members of
- Parent, and adds a few more.
-
- b = NEW Parent() && prints 'first '
- ? b.X && prints 10
- ? b.Y && prints 20
-
- d = NEW Derived() && prints 'first second'
- ? d.X && prints 10
- ? d.Y && prints 20
- ? d.Z && prints 100
-
- CLASS Parent
- MEMBER X, Y
- ? "first "
- X = 10
- Y = 20
- ENDCLASS
-
- CLASS Derived OF Parent
- MEMBER Z
- ?? "second"
- Z = 100
- ENDCLASS
-
- Overriding Parent Members - Polymorphism
-
- When you derive a new class from a parent class, you can override
- parent members by simply re-initializing the variables or re-
- declaring the subroutines.
-
- The technique of overriding parent members is an example of
- polymorphism. Polymorphism lets you treat similar objects in a
- uniform fashion.
-
- This example defines a Parent class and derives a class from
- Parent, overriding one of Parent's methods:
-
- A = NEW Parent()
- B = NEW Derived()
- ? A.One() && Prints 'Parent 1'
- ? A.Two() && Prints 'Parent 2'
- ? B.One() && Prints 'Parent 1'
- ? B.Two() && Prints 'Derived 2'
-
- CLASS Parent
- FUNCTION One
- RETURN "Parent 1"
- FUNCTION Two
- RETURN "Parent 2"
- ENDCLASS
-
- CLASS Derived OF Parent
- FUNCTION Two && Override Method Two
- RETURN "Derived 2"
- ENDCLASS
-
- Arrays are Objects
-
- An array is a homogenous collection of variables; an object is a
- heterogeneous collection.
-
- Arrays are a kind of object. Arrays use the index operator to
- refer to contents. Arrays also have properties. An array has the
- two built-in properties, size and dimensions. Arrays are objects,
- so variables that refer to arrays are of type object reference.
- More than one variable can refer to the same array.
-
- decl a[10]
- ? a.size && prints 10
- ? a[1] && prints false
- a[1] = 10
- b = a && b and a refer to the same array
- ? b[1] && prints 10
- b[1] = 20
- ? a[2] && prints 20
- ? a.fill("hello")
- ? a[4] && prints 'hello'
-
- Like all other objects, arrays are extensible. Variables can be
- added to arrays via assignment. For example:
-
- decl a[10]
- a.myprop = "hello" && add variable 'myprop' to
- ? a.myprop && prints "hello"
-
- Passing Properties by Reference
-
- Pass properties by reference the same way you pass other memory
- variables. You can also pass array elements by reference.
-
- O = NEW Object()
- O.X = 20
- DO Func WITH O.X
- ? O.X && prints 30
-
- PROCEDURE Func
- PARAMETERS Y
- Y = Y + 10
- RETURN
-
- Returning Object-Reference Variables in Functions
-
- Object-reference is a valid return type for a function. For
- example:
-
- O = MakeObject() && Creates a new object
- ? O.X && Returns 10
-
- FUNCTION MakeObject
- PRIVATE x
- x = NEW Object()
- x.x = 10
- RETURN x && Returns an object-reference var
-
- Passing Parameters to Base Classes
-
- You can pass parameters to base classes by including them in
- parenthesis after the class name. For example:
-
- NOTE: The following code will not execute in this Alpha release.
-
- CLASS MyCube(n) OF MySquare(n)
- MEMBER CubeValue
- CubeValue = Num * Square
- ENDCLASS
-
- CLASS MySquare(n)
- MEMBER Num, Square
- Num = n
- Square = n*n
- ENDCLASS
-
-